package vsqlbuilder.gui.diagram;

import static vsqlbuilder.gui.MainController.ARROW;
import static vsqlbuilder.gui.MainController.JOIN;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.List;

import javax.swing.JPanel;

import vsqlbuilder.logic.Column;

public class DiagramView extends JPanel {

	static final long serialVersionUID = 0L; // TODO [low]: temporary fix,
												// must be change to correct one

	private List<IGshape> elements;
	private DiagramController controller;
	private DiagramModel model;
	private IGshape shapeSelected;
	private int diffX, diffY;
	private Point p = null;
	private Column colSourceSelected,colDestSelected;
	private GConnectionLine simpleLine;
	
	private Point colSource; //TODO [low]: try to delete this is temporary variable should be done a long term fix
	
	public int menuOption;  
	
	
	
	
	
	

	public DiagramView(Dimension size, List<IGshape> elements) { // DiagramController
																	// controller
		super();
		setPreferredSize(size);
		setBackground(Color.white);
		this.elements = elements;
		shapeSelected = null;
		setFocusable(true); // [BUG fix #4] Part 1/3 - default focusability for
							// JPanel is false, so you need to include this line
							// to do work keyboard events
		menuOption=ARROW;
		colSourceSelected=null;
		simpleLine = new GConnectionLine();
	}

	// [BUG fix #4] Part 2/3 - default focusability for JPanel is false, so you
	// need to include this line to do work keyboard events
	public boolean isFocusable() {
		return true;
	}

	public void registerDiagramController(DiagramController dc) {
		this.controller = dc;
	}

	public void registerModel(DiagramModel m) {
		this.model = m;
	}
	
	// IMPORTANT: This method cannot be used to modified the model view, ONLY
	// for font metrics purposes.
	public Graphics2D getMyGraphics() { // NOTE: never call this method
										// getGraphics can override by mistake
										// native an create big trouble ;)
		return (Graphics2D) getGraphics();
	}

	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D) g;

		// Here draw all graphics objects in application list
		for (IGshape element : this.elements) {
			if (element instanceof GTable){
				GTable gt = (GTable) element;
				for(GAnchorPoint ap : gt.getAnchorPointsList()){
					for(GJoinRelation jr : ap.getJoinsList()){
						jr.paint(g2);
					}
				}
				element.paint(g2);				
			}
				

		}
		
		// Here Draw connection line of joins, only if needed
		if (this.simpleLine.isVisible()) {
		    this.simpleLine.paint(g2);
		}
	}

	public void eVmousePressed(MouseEvent ev) {
		p = ev.getPoint();
		IGshape selected = controller.getShapeAt(p.x, p.y);
		switch (this.menuOption) {
		case ARROW:
			if (selected != null) {
				//CASE Of GJoinRelation or GTable
				if ((selected instanceof GTable) || (selected instanceof GJoinRelation)) {
					// Unselect another table selected previously
					if ((shapeSelected != null) && (selected != shapeSelected)){
						shapeSelected.setSelected(false);						
					}
					shapeSelected = selected;
					shapeSelected.setSelected(true);
					if(selected instanceof GTable){  //Select or unselect columns & add or remove from query
						processColumnClick((GTable)shapeSelected,p);
					}
					//Case of GJoinRelation do nothing
				}

			} else { // if no table or join link is selected unselect all tables
				// or if exists any select element then unselect
				if(shapeSelected!=null){
					shapeSelected.setSelected(false);
				}
				shapeSelected = null;
				this.diffX = 0;
				this.diffY = 0;
				//TODO [low]: Research if this for is needed or not?
				for (IGshape element : this.elements) {
					if (element.getSelected() == true) {
						element.setSelected(false);
					}
				}
			}

			break;
		case JOIN:
			//if a table was probably selected
			if ((selected != null)) {
				if ( (selected instanceof GTable) && (this.colSourceSelected==null) ) {
					GTable gtselected = (GTable)selected;
					processJoinSource(gtselected); //After call this procedure the colSourceSelected must not be null 
					if(colSourceSelected!=null){
						//Here Calculate location x,y of source in the middle of column name, but must choose between right or left
						Point p=gtselected.getJoinAtColPos(colSourceSelected,null);
						simpleLine.show(p);
						simpleLine.setSource(gtselected);
					}
				}else{
					this.diffX = 0;  //TODO [low]: research if this two lines are needed
					this.diffY = 0;
				}
			}
			break;
		}

		this.repaint();
	}

	public void eVmouseDragged(MouseEvent ev) {
		Graphics2D g = (Graphics2D) getGraphics();
		switch (this.menuOption) {
		case ARROW:
			int x = ev.getPoint().x;
			int y = ev.getPoint().y;
			if (shapeSelected != null) {
				shapeSelected.move(diffX, diffY, x, y);
				shapeSelected.paint(g);
			}
			break;
		case JOIN:
			//TODO [HIGH]: her must draw partial line to destination column of join
			if(this.colSourceSelected!=null  && this.colSource!=null){
				this.simpleLine.resize(ev.getX(), ev.getY());
			}
			break;
		}
		this.repaint();
		g.dispose();
	}

	public void eVmouseReleased(MouseEvent ev) {
		p = ev.getPoint();
		IGshape selected = controller.getShapeAt(p.x, p.y);
		switch (this.menuOption) {
		case ARROW:
			break;
		case JOIN:
			//If source column of join was selected but destination no
			if((colSourceSelected!=null) && (colDestSelected==null)){
				GTable gtselected =(GTable)selected;
				this.processJoinDest(gtselected);
				String sourceSchemaTableName=colSourceSelected.getParentTable().getSchemaName()+colSourceSelected.getParentTable().getName();
				String destSchemaTableName=colDestSelected.getParentTable().getSchemaName()+colDestSelected.getParentTable().getName();
				//TODO [low] optimize this part, fix source code mess of join links, do it in a more organized way and simplify
				if( (colSourceSelected != null) && (colDestSelected != null) ){
					if(!sourceSchemaTableName.equalsIgnoreCase(destSchemaTableName)){ //TODO [medium]: in a future allow to do self selection of columns in some way [probably not same table but a copy maybe] 
						System.out.println("DEBUG INFO: A Join [in table: "+gtselected.getShapeLogic().getName()+"] was create between columns: "+colSourceSelected.getName()+" to "+colDestSelected.getName());
						//Set Position of origin temporary link in Column
						Point coordinates=null;
						GTable gtsourcesel= this.simpleLine.getSource();
						Point p = new Point(gtselected.getRectangle().x,gtselected.getRectangle().y);
						coordinates=gtsourcesel.getJoinAtColPos(colSourceSelected, p);
						simpleLine.show(coordinates.x, coordinates.y);
					
						//Set Position of temporary destination link in Column
						p = simpleLine.getSourcePoint();
						coordinates=gtselected.getJoinAtColPos(colDestSelected, p);
						simpleLine.resize(coordinates.x, coordinates.y);
						this.repaint();

						//Set real link in anchors of Gtables source & destination
						GAnchorPoint apsource=gtsourcesel.getAnchorPoint(colSourceSelected);
						GAnchorPoint apdest=gtselected.getAnchorPoint(colDestSelected);
						
						//Add GJoinRelation to the model [Inside source GTable]
						this.model.addJoin(apsource, apdest,gtsourcesel,colSourceSelected);
					}else{
						this.repaint(); //Eliminate line from self relation
					}
				}
				//After process the collected information must reset source/destination column variables and hide temporary join line
				this.colDestSelected=null;
				this.colSourceSelected=null;
				this.simpleLine.setSource(null);
				this.simpleLine.hide();
			}
			break;
		}
	}
	
	public void eVkeyPressed(KeyEvent e) {
		if (e.getKeyCode() == java.awt.event.KeyEvent.VK_DELETE) {
			// Remove first selected tables or join relations from query
			this.controller.removeTables();
			
			//TODO [URGENT]: Fix Delete from model, because now we should be careful with joins [Stored in Source] because source can be destroyed or the table join points can be destroyed.
			
			this.controller.removeJoinLinks();
			this.repaint();
		}
	}
	
	private void processColumnClick(GTable gt, Point p){
		// Calculate distance from table x,y left top corner to
		// click point
		this.diffX = (p.x - shapeSelected.getRectangle().x);
		this.diffY = (p.y - shapeSelected.getRectangle().y);
		// Here calculate the column that was pressed
		Column colSelected = shapeSelected
				.getColumnAt(diffX, diffY);
		if (colSelected != null) {
			if (colSelected.getQuerySelected()) { // if the Column
				// was
				// previously selected
				// must unselect and
				// remove from query
				colSelected.setQuerySelected(false);
				controller.removeColumnFromQuery(colSelected);
			} else { // Column is set as Selected, because that I
				// must
				// add to query
				colSelected.setQuerySelected(true);
				controller.addColumnToQuery(colSelected);
			}
		}
	}
	
	private void processJoinSource(GTable tsour){
		//Get First Column of join [source]
		// Calculate distance from table x,y left top corner to
		// click point
	 if(tsour!=null){
		this.diffX = (p.x - tsour.getRectangle().x);
		this.diffY = (p.y - tsour.getRectangle().y);
		// Here calculate the column that was pressed
		colSourceSelected = tsour.getColumnAt(diffX, diffY);
		this.colSource=p;
	 	}
	}
	

	private void processJoinDest(GTable tdest){
		//Get Second Column of join [destination]
		// Calculate distance from table x,y left top corner to
		// click point
	if(tdest!=null){
		this.diffX = (p.x - tdest.getRectangle().x);
		this.diffY = (p.y - tdest.getRectangle().y);
		colDestSelected = tdest.getColumnAt(diffX, diffY);
		}
	}
	
}
